package com.hzlj.position.locate.differentiation.sysCheck.traceCheck;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fz.common.base.core.JsonUtils;
import com.hzlj.common.message.common.dto.message.MessageNoticeResultDTO;
import com.hzlj.position.config.common.enums.ReportType;
import com.hzlj.position.config.common.enums.WarningType;
import com.hzlj.position.config.service.NoticeConfigService;
import com.hzlj.position.locate.common.common.PositionSimpleDTO;
import com.hzlj.position.locate.common.dto.differentiationConfig.DifferentiationConfigExtDTO;
import com.hzlj.position.locate.common.dto.differentiationData.DifferentiationDataAddDTO;
import com.hzlj.position.locate.common.dto.differentiationData.DifferentiationDataQueryResultDTO;
import com.hzlj.position.locate.common.dto.position.PositionExtDTO;
import com.hzlj.position.locate.common.dto.position.PositionKeyDTO;
import com.hzlj.position.locate.common.dto.position.PositionQueryResultDTO;
import com.hzlj.position.locate.common.dto.positionSysCheck.PositionSysCheckExtDTO;
import com.hzlj.position.locate.common.dto.positionSysCheck.PositionSysCheckQueryResultDTO;
import com.hzlj.position.locate.common.dto.reportInspection.ReportInspectionExtDTO;
import com.hzlj.position.locate.common.enums.PositionSysCheck;
import com.hzlj.position.locate.common.model.Position;
import com.hzlj.position.locate.common.model.ReportInspection;
import com.hzlj.position.locate.converter.PositionSysCheckConverter;
import com.hzlj.position.locate.dao.ReportInspectionDao;
import com.hzlj.position.locate.differentiation.DifferentiationService;
import com.hzlj.position.locate.differentiation.common.PositionSysCheckHandle;
import com.hzlj.position.locate.differentiation.common.PositionSysCheckNoticeGzry;
import com.hzlj.position.locate.service.DifferentiationDataService;
import com.hzlj.position.locate.service.PositionService;
import com.hzlj.position.locate.service.PositionSysCheckService;
import com.hzlj.position.locate.service.ReportInspectionSyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * 公共资源
 */
@Slf4j
@Service
public class TraceSysCheckCommon {
    @Resource
    private PositionSysCheckHandle positionSysCheckHandle;
    @Resource
    private PositionSysCheckService positionSysCheckService;
    @Resource
    private PositionSysCheckNoticeGzry positionSysCheckNoticeGzry;
    @Resource
    private ReportInspectionSyncService reportInspectionSyncService;
    @Resource
    private ReportInspectionDao reportInspectionDao;
    @Resource
    private NoticeConfigService noticeConfigService;
    @Resource
    private PositionService positionService;
    @Resource
    private DifferentiationDataService differentiationDataService;


    /**
     * 第一步：通知矫正对象上报
     */
    public void noticeJzry(List<Position> positions,
                           Map<String, MessageNoticeResultDTO> notices,
                           DifferentiationService differentiationService) {
        Date now = new Date();
        //发起人工核验
        List<ReportInspection> inspections = positions.stream().map(e -> {
            ReportInspection inspection = reportInspectionSyncService.defaultConvert(e.getJzId(), e.getJgCode(), now, ReportType.TRACK_CHECK);
            inspection.setExt(
                    new ReportInspectionExtDTO(e.getId(), e.getPositionTime())
            );
            MessageNoticeResultDTO notice = notices.get(e.getId());
            if (notice != null) {
                log.info("[位置确认]发起位置上报消息结果:originPositionId={}", e.getId());
                inspection.setNotice(!notice.isFail());
                inspection.setNoticeFailMsg(notice.isFail() ? notice.getStatusMsg() : null);
            } else {
                log.warn("[位置确认]发起位置上报消息结果为空:originPositionId={}", e.getId());
            }
            return inspection;
        }).collect(Collectors.toList());
        this.reportInspectionDao.saveBatch(inspections);

        //通知工作人员位置确认结果
        for (Position position : positions) {
            int delayMinutes = noticeConfigService.trackCheckDelayMinutes(position.getJgCode());
            DateTime noticeTime = DateUtil.offset(new Date(), DateField.MINUTE, delayMinutes);
            this.positionSysCheckNoticeGzry.noticeNoReport(position, noticeTime, differentiationService.getNoticeWorkerRoles());
        }

        //修改原始记录
        List<PositionKeyDTO> keys = positions.stream().map(e -> new PositionKeyDTO(e.getId(), e.getPositionTime())).collect(Collectors.toList());
        positionSysCheckHandle.handleSysCheck(keys, PositionSysCheck.ING);

        //保存第一步的结果
        DifferentiationConfigExtDTO config = differentiationService.getDifferentiationConfigExt();
        positionSysCheckService.savePositionSysCheck(differentiationService.getType(),
                positions.stream().map(e -> {
                    MessageNoticeResultDTO notice = notices.get(e.getId());
                    return PositionSysCheckConverter.of(
                            e.getJzId(),
                            new PositionKeyDTO(e.getId(), e.getPositionTime()),
                            "notice",
                            config.getTraceCheck().getNoticeJzryStep(),
                            notice != null && !notice.isFail(),
                            notice != null ? notice.getStatusMsg() : "通知异常"
                    );
                }).collect(Collectors.toList())
        );
    }


    /**
     * 第二步：矫正人员上报
     *
     * @param report         上报位置
     * @param originPosition 原始记录
     */
    public void updateReportResult(String jzId,
                                   String jgCode,
                                   PositionSimpleDTO report,
                                   PositionSimpleDTO originPosition,
                                   DifferentiationService differentiationService) {
        //将上报位置更新到原始记录中
        PositionExtDTO positionExt = this.positionService.getPositionExt(originPosition.getId(), originPosition.getPositionTime());
        positionExt.setTrackCheckReportPosition(report);
        this.positionService.updatePositionExt(originPosition.getId(), originPosition.getPositionTime(), positionExt);

        //是否已经存在通知结果
        PositionSysCheckQueryResultDTO resultNotice = positionSysCheckService.getPositionSysCheck(differentiationService.getType(), originPosition.getId(), "result_notice");
        if (ObjectUtil.isNotEmpty(resultNotice)) {
            log.info("[位置确认]已经存在结果通知步骤，不需要重复通知:service={},originPositionId={},resultNotice={}",
                    differentiationService.getClass().getSimpleName(), originPosition.getId(), resultNotice);
            return;
        }

        //考虑到入库是异步的，所以需要这样处理
        this.differentiationDataService.saveDifferentiationData(new DifferentiationDataAddDTO()
                .setType(differentiationService.getType().getDataType())
                .setHandleTime(DateUtil.offset(new Date(), DateField.SECOND, 60))
                .setServiceId(report.getId())
                .setJzId(jzId)
                .setJgCode(jgCode)
                .setData(JsonUtils.obj2json(new TraceSysCheck.DifferentiationData(report, originPosition)))
        );
    }


    public void handleDifferentiationData(DifferentiationDataQueryResultDTO data,
                                          DifferentiationService differentiationService,
                                          BiConsumer<Boolean, PositionKeyDTO> reportKeyHandler) {
        String jzId = data.getJzId();
        TraceSysCheck.DifferentiationData dto = JsonUtils.json2obj(data.getData(), TraceSysCheck.DifferentiationData.class);
        PositionSimpleDTO report = dto.getReport();
        PositionSimpleDTO oriPosition = dto.getOriPosition();
        boolean normal = report.getWarningType().equals(WarningType.SUCCESS.getCode());
        DifferentiationConfigExtDTO config = differentiationService.getDifferentiationConfigExt();


        log.info("[位置确认]处理位置上报信息:service={},normal={},originPosition={},reportPosition={}",
                differentiationService.getClass().getSimpleName(), normal, oriPosition, report);

        //原始记录
        PositionKeyDTO originKey = new PositionKeyDTO(oriPosition.getId(), oriPosition.getPositionTime());
        if (normal) {
            positionSysCheckHandle.handleNormal(originKey, config.getTraceCheck().getNormalRemark());
        } else {//不做处理，只更系统核验标记
            positionSysCheckHandle.handleSysCheck(originKey, PositionSysCheck.FAIL);
        }

        //核查记录的处理
        PositionKeyDTO reportKey = new PositionKeyDTO(report.getId(), report.getPositionTime());
        if (reportKeyHandler != null) {
            reportKeyHandler.accept(normal, reportKey);
        }

        //发送通知
        Map<String, MessageNoticeResultDTO> notices = positionSysCheckNoticeGzry.notice(normal, originKey, differentiationService.getNoticeWorkerRoles());

        //平台核查-矫正对象上报
        saveJzryReportStep(jzId, normal, originKey, reportKey, differentiationService);

        //平台核查-结果通知
        MessageNoticeResultDTO notice = notices.get(originKey.getId());
        saveNoticeGzryStep(jzId, originKey, notice, differentiationService);
    }


    /**
     * 第三步：发送未上报结果
     */
    public void updateNoReportResult(PositionQueryResultDTO position,
                                     MessageNoticeResultDTO notice,
                                     DifferentiationService differentiationService) {
        PositionKeyDTO originKey = new PositionKeyDTO(position.getId(), position.getPositionTime());
        //更新为未上报
        positionSysCheckHandle.handleSysCheck(originKey, PositionSysCheck.NOT_REPORT);

        //平台核查-未上报
        saveJzryReportStep(position.getJzId(), false, originKey, null, differentiationService);

        //平台核查-结果通知
        saveNoticeGzryStep(position.getJzId(), originKey, notice, differentiationService);
    }

    private void saveJzryReportStep(String jzId,
                                    Boolean normal,
                                    PositionKeyDTO originKey,
                                    PositionKeyDTO reportKey,
                                    DifferentiationService differentiationService) {
        DifferentiationConfigExtDTO config = differentiationService.getDifferentiationConfigExt();
        String msg = normal ? "已上报-正常" : "已上报-异常";
        if (reportKey == null) {
            msg = "未上报";
        }
        //平台核查-矫正对象上报
        positionSysCheckService.savePositionSysCheck(differentiationService.getType(),
                PositionSysCheckConverter.of(
                        jzId,
                        originKey,
                        reportKey,
                        "report",
                        config.getTraceCheck().getJzryReportStep(),
                        normal,
                        msg
                )
        );
    }

    private void saveNoticeGzryStep(String jzId,
                                    PositionKeyDTO originKey,
                                    MessageNoticeResultDTO notice,
                                    DifferentiationService differentiationService) {
        DifferentiationConfigExtDTO config = differentiationService.getDifferentiationConfigExt();
        positionSysCheckService.savePositionSysCheck(differentiationService.getType(),
                PositionSysCheckConverter.of(
                        jzId,
                        originKey,
                        "result_notice",
                        config.getTraceCheck().getNoticeGzryStep(),
                        notice != null && !notice.isFail(),
                        notice != null ? notice.getStatusMsg() : "通知异常"
                ).setSysCheckExt(notice == null ? null : new PositionSysCheckExtDTO()
                        .setMessageId(notice.getId() == null ? null : notice.getId().toString())
                        .setMessageServiceId(notice.getServiceId())
                        .setMessageTemplate(notice.getMessageTemplate())
                )
        );
    }
}
